package com.ilarele.shopping.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

import com.ilarele.shopping.Message;
import com.ilarele.shopping.Product;
import com.ilarele.shopping.ProductComment;
import com.ilarele.shopping.ProductInfo;
import com.ilarele.shopping.ProductName;
import com.ilarele.shopping.ProductPicture;
import com.ilarele.shopping.ProductTag;
import com.ilarele.shopping.server.tags.TagInfo;
import com.ilarele.shopping.server.tags.TagsRecommendation;
import com.ilarele.shopping.server.trust.TrustAlgorithm;

public class ShoppingServer {
	private ServerSocket server;
	private ServerSocket getImageServer;
	private ServerSocket sendImageServer;

	private MysqlBasicOps serverMysqlConnection;
	private TrustAlgorithm trustAlgorithm;
	private TagsRecommendation tagsRecommend;

	public ShoppingServer() {
		try {
			serverMysqlConnection = new MysqlBasicOps(this);
			trustAlgorithm = new TrustAlgorithm(serverMysqlConnection);
			server = new ServerSocket(ConnectionConstants.PORT);
			getImageServer = new ServerSocket(
					ConnectionConstants.IMAGE_GET_PORT);
			sendImageServer = new ServerSocket(
					ConnectionConstants.IMAGE_SEND_PORT);
			tagsRecommend = new TagsRecommendation(serverMysqlConnection);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		// MysqlConnection conn = new MysqlConnection();
		// conn.dropTables();
		final ShoppingServer example = new ShoppingServer();
		new Thread(new Runnable() {
			@Override
			public void run() {
				example.handleConnection();
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				example.getImages();
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				example.sendImages();
			}
		}).start();
	}

	protected void getImages() {
		System.out.println("Waiting for images...");
		while (true) {
			try {
				Socket socket = getImageServer.accept();
				new DownloadImage(socket);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	protected void sendImages() {
		System.out.println("Send images...");
		while (true) {
			try {
				Socket socket = sendImageServer.accept();
				new SendImage(socket);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void handleConnection() {
		System.out.println("Waiting for client message...");
		while (true) {
			try {
				Socket socket = server.accept();
				new ConnectionHandler(socket, serverMysqlConnection,
						trustAlgorithm, tagsRecommend);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void incFriendship(String idClient, String idFriend, int points) {
		trustAlgorithm.incFriendship(idClient, idFriend, points);
	}

	public HashMap<String, Float> getTagsPrefForTag(String tag) {
		return tagsRecommend.getTagsPrefForTag(tag);
	}

	public void incTagOccurrence(String tag) {
		tagsRecommend.incTagOccurrence(tag);
	}

	public TagInfo getTagPref(String tag) {
		return tagsRecommend.getTag(tag);
	}

	public void saveTagPref(String tag, HashMap<String, Float> relatedTags,
			int occ) {
		tagsRecommend.saveTagPref(tag, relatedTags, occ);
	}
}

class ConnectionHandler implements Runnable {
	private Socket socket;
	private MysqlBasicOps dbConnection;
	private TrustAlgorithm trustAlgorithm;
	private TagsRecommendation tagsRecommend;

	public ConnectionHandler(Socket socket,
			MysqlBasicOps serverMysqlConnection, TrustAlgorithm trustAlgorithm,
			TagsRecommendation tagsRecommend) {
		this.socket = socket;
		this.dbConnection = serverMysqlConnection;
		this.trustAlgorithm = trustAlgorithm;
		this.tagsRecommend = tagsRecommend;

		Thread t = new Thread(this);
		t.start();
	}

	public void run() {
		String idClient = null;
		try {
			System.out.print("New Request from ");
			ObjectInputStream ois = new ObjectInputStream(
					socket.getInputStream());
			ObjectOutputStream oos = new ObjectOutputStream(
					socket.getOutputStream());

			Message message = (Message) ois.readObject();
			idClient = message.idClient;
			Product prod;
			switch (message.type) {
			case Message.TYPE_CLIENT_ADD_PROD:
				System.out.println("TYPE_CLIENT_ADD_PROD");
				message.content = new Product();
				if (dbConnection.existsProd(message.idProd,
						(Product) message.content)) {
					message.type = Message.TYPE_SERVER_ADD_PROD_EXISTS;
				} else {
					dbConnection.addNewProduct(message.idProd, idClient);
					message.type = Message.TYPE_SERVER_ADD_PROD_NEW;
				}
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_FILTER_PRODS:
				System.out.println("TYPE_CLIENT_FILTER_PRODS");
				dbConnection.addUser(idClient);
				trustAlgorithm.addUser(idClient);
				if (message.content == null) {
					message = new Message();
					ArrayList<Product> prodList = dbConnection.getProducts();
					// filter 1 - friends
					trustAlgorithm.addPointsByFriendship(prodList, idClient);
					// filter 2 - tags
					tagsRecommend.addPointsByTags(prodList, idClient);

//					// sort result
//					Collections.sort(prodList, new Comparator<Product>() {
//						@Override
//						public int compare(Product o1, Product o2) {
//							return Float
//									.compare(o2.pointsTrust, o1.pointsTrust);
//						}
//					});
//
//					System.out.println("By Trust");
//					for (Product product : prodList) {
//						System.out.println("\t" + product.pName.name + " "
//								+ product.pointsTrust);
//					}
//
//					Collections.sort(prodList, new Comparator<Product>() {
//						@Override
//						public int compare(Product o1, Product o2) {
//							return Float.compare(o2.pointsTags, o1.pointsTags);
//						}
//					});
//					System.out.println("By Tags");
//					for (Product product : prodList) {
//						System.out.println("\t" + product.pName.name + " "
//								+ product.pointsTags + "(" + product.pTags.tags
//								+ ")");
//					}

					Collections.sort(prodList, new Comparator<Product>() {
						@Override
						public int compare(Product o1, Product o2) {
							return Float.compare(
									o2.pointsTags + o2.pointsTrust,
									o1.pointsTags + o1.pointsTrust);
						}
					});
//					System.out.println("All");
//					for (Product product : prodList) {
//						System.out.println("\t" + product.pName.name + " "
//								+ (product.pointsTags + product.pointsTrust)
//								+ "(" + product.pTags.tags + ")");
//					}
					// result
					message.content = prodList;
				}
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_INIT_PROD:
				System.out.println("TYPE_CLIENT_INIT_PROD");
				dbConnection.saveProd((Product) message.content, idClient);
				break;
			case Message.TYPE_CLIENT_RATE_INFO:
				dbConnection.updateProdInfo((ProductInfo) message.content,
						idClient);
				break;
			case Message.TYPE_CLIENT_RATE_TAGS:
				dbConnection.updateProdTags((ProductTag) message.content,
						idClient);
				break;
			case Message.TYPE_CLIENT_RATE_NAME:
				dbConnection.updateProdName((ProductName) message.content,
						idClient);
				break;
			case Message.TYPE_CLIENT_RATE_PICTURE:
				dbConnection.updateProdPicture(
						(ProductPicture) message.content, idClient);
				break;

			case Message.TYPE_CLIENT_NEXT_RATE_NAME:
				prod = (Product) message.content;
				if (dbConnection.getNextProdName(1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_PREV_RATE_NAME:
				prod = (Product) message.content;
				if (dbConnection.getNextProdName(-1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_NEXT_RATE_INFO:
				prod = (Product) message.content;

				if (dbConnection.getNextProdInfo(1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_PREV_RATE_INFO:
				prod = (Product) message.content;
				if (dbConnection.getNextProdInfo(-1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_NEXT_RATE_TAGS:
				prod = (Product) message.content;
				if (dbConnection.getNextProdTags(1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_PREV_RATE_TAGS:
				prod = (Product) message.content;
				if (dbConnection.getNextProdTags(-1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_POST_COMMENT:
				prod = (Product) message.content;
				System.out.println("recev id " + prod.idProd);
				dbConnection.insertComment(prod, message.idClient);
				break;
			case Message.TYPE_CLIENT_GET_COMMENTS:
				ArrayList<ProductComment> list = dbConnection
						.getComments((String) message.content);
				message.content = list;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_NEXT_RATE_PICTURE:
				prod = (Product) message.content;
				if (dbConnection.getNextProdPic(1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;
			case Message.TYPE_CLIENT_PREV_RATE_PICTURE:
				prod = (Product) message.content;
				if (dbConnection.getNextProdPic(-1, prod)) {
					message.content = prod;
				} else
					message.content = null;
				oos.writeObject(message);
				break;

			default:
				System.out.println("Unknown message type: " + message.type
						+ " " + message.toString());
				break;
			}

			ois.close();
			oos.close();
			socket.close();
			System.out.println("Waiting for client message...");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}
